// 1. 模块与私有变量的实现 的 原理
// 私有变量放在对象里 不安全
// 私有变量放在构造函数和原型对象里 开销比较大，原型上也不安全
// 私有变量放在立即执行函数重 好的实现
;(function ($, window, document) {
  function go(num) {}

  function handleEvents() {}

  function initialize() {}

  function dieCarouselDie() {}

  //attach to the global scope
  window.finalCarousel = {
    init: initialize,
    destroy: dieCarouselDie,
  }
})(jQuery, window, document)
// 也称为模块的放大模式 对输入的模块进行拓展，就是放大

// 2. 构造函数的缺点，带来了 prototype 的原型 模型

// 3. prototype 的原型链模型，带来了 constructor 属性
// 修改 原型对象的时候，一般要同时修改 constructor 的指向 ，因为修改原型对象会改变 原来原型对象的 constructor 指针，导致 instanceof 失真
C.prototype = {
  constructor: C, // 保证 instanceof 不会失真
  method1: function () {},
  // ...
}
// or
C.prototype.constructor = C

// 4. 引发了构造函数继承 多重继承等问题 从而带来了 Mixin 模式
function M1() {
  this.hello = 'hello'
}

function M2() {
  this.world = 'world'
}

function S() {
  M1.call(this)
  M2.call(this)
}

// 继承 M1
S.prototype = Object.create(M1.prototype)
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype)

// 指定构造函数
S.prototype.constructor = S

var s = new S()
s.hello // 'hello'
s.world // 'world'

// 实现一个 Mixin 模式
let mix = (object) => ({
  with: (...mixins) =>
    mixins.reduce(
      (accumulator, mixin) =>
        Object.create(accumulator, Object.getOwnPropertyDescriptors(mixin)),
      object
    ),
})

// 5. 必须了解的 instanceof new 原理 每次改写 prototype 对象后，必须要重新指定 constructor 属性
// 否则 instanceof 会失真，那么 唯一让 instanceof 失真的情况是 Object.create(null)
// instanceof 只能用在 objcet 上
function myInstanceof(left, right) {
  while (true) {
    if (left.__proto__ === null) return false
    if (left.__proto__ === right.prototype) return true
    left = left.__proto__
  }
}
function myInstanceof(left, right) {
  while (true) {
    if (Object.getPrototypeOf(left) === null) return false
    if (Object.getPrototypeOf(left) === right.prototype) return true
    left = Object.getPrototypeOf(left)
  }
}

function myNew(func) {
  let obj = Object.create(func.prototype)
  const result = func.apply(this, arguments)
  if (
    result &&
    (typeof result === 'object' || typeof result === 'function') &&
    result !== null
  ) {
    return result
  }
  return obj
}
// 调用构造函数忘记加 new 怎么办
function autoAddNew() {
  if (this instanceof autoAddNew) {
    this.x = 1
    // ...
  } else {
    return new autoAddNew()
  }
}

// Object.create() 原理    Object.create(arg1, arg2)   arg1 是原型对象   arg2 是 属性描述对象
if (typeof Object.create !== 'function') {
  Object.create = function (obj) {
    function F() {}
    F.prototype = obj
    return new F()
  }
}
// 获取对象的所有属性
function inheritedPropertyNames(obj) {
  let props = {}
  while (obj) {
    Object.getOwnPropertyNames(obj).forEach((name) => {
      props[name] = true
    })
    obj = Object.getPrototypeOf(obj)
  }
  return Object.getOwnPropertyNames(props)
}
inheritedPropertyNames(Date)
// 对象的拷贝
// 与原对象具有相同的原型 相同的实例属性
function copyObject(orig) {
  let copy = Object.create(Object.getPrototypeOf(orig))
  copyOwnPropertiesFrom(copy, orig)
  return copy
}
// 复制原对象的所有实例属性
function copyOwnPropertiesFrom(target, source) {
  Object.getOwnPropertyNames(source).forEach(function (propKey) {
    let desc = Object.getOwnPropertyDescriptor(source, propKey)
    Object.defineProperty(target, propKey, desc)
  })
  return target
}
// or
function copyObject(orig) {
  return Object.create(
    Object.getPrototypeOf(orig),
    Object.getOwnPropertyDescriptors(orig)
  )
}

// 对象的方法
// 静态方法
Object.is()
Object.assign()
Object.fromEntries()
// 继承
Object.create()
Object.getPrototypeOf()
Object.setPrototypeOf()
// 属性描述对象
Object.getOwnPropertySymbols()
Object.getOwnPropertyNames()
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptors()
// 修改属性
Object.defineProperty()
Object.defineProperties()
// 状态类
Object.preventExtensions()
Object.isExtensible()
Object.freeze()
Object.isFrozen()
Object.seal()
Object.isSealed()
// 遍历
Object.keys()
Object.entries()
Object.values()

// 实例方法
Object.prototype.toString()
Object.prototype.valueOf()

// Object.is() es5 部署
Object.defineProperty(Object, 'is', {
  value: function (x, y) {
    if (x === y) {
      return 1 / x === 1 / y || x !== 0
    }
    return x !== x && y !== y
  },
  configurable: true,
  enumerable: false,
  writable: true,
})

// 自动转对象 调用的是 Object 这个函数
// String Number Boolean 会产生装箱操作
// ;[[PrimitiveValue]] 原始值

// 会自动调用 Object 函数的 Object上的静态方法有哪些呢？
Object.setPrototypeOf()
Object.getPrototypeOf()
// undefined null 作为这俩方法的第一个参数会报错

// 不包含继承，所有可遍历属性 三个方法返回的都是遍历器
// 键名 键值 键值对数组
// keys values entries

Object.entries() // 对象转为键值对数组
Object.fromEntries() // 键值对数组转为对象. Map 就是一个键值对数组结构的对象

let entries = new Map([
  ['foo', 'bar'],
  ['baz', 42],
])
// or
entries = [
  ['foo', 'bar'],
  ['baz', 42],
]

Object.fromEntries(entries)
// { foo: "bar", baz: 42 }

// 查询字符串转为对象
Object.fromEntries(new URLSearchParams('foo=bar&baz=qux')) // {foo: 'bar', baz: 'qux'}
